跳到主要内容

16 混淆指针与数组的问题

下面的程序输出什么?

//define.c
#include <stdio.h>
char g_name[] = "Hello World!";
void define_print(){
printf("define_print():%s\n",g_name);
}
//main.c
#include <stdio.h>
extern char* g_name;
int main(){
define_print();
printf("main():%s\n",g_name);
return 0;
}

实例分析:指针==数组?

//define.c
#include <stdio.h>

char g_name[] = "Hello World!";

void define_print()
{
printf("define_print() : %s\n", g_name);
}

//main.c
#include <stdio.h>

extern char* g_name;

int main()
{
define_print();

printf("main() : %s\n", g_name);

return 0;
}

再论指针和数组

  • 指针

    • 本质为变量,保存的目标值为内存地址
    • 指针运算与*操作符配合使用能够模拟数组行为
  • 数组

    • 数组是一段连续的存储空间
    • 数组名可看做指向数组第一个元素的常量指针
  • 在C语言层面中的等价关系

    int a[3] = {0};
    int *p = a;
    p[0] = 1; //a[0]=1
    p[1] = 2; //a[1]=2
    a[2] = 3; //p[2]=3
  • 问题

    在二进制层面,指针和数组是否等价?

编程实验:数组与指针深度分析

#include <stdio.h>

int test()
{
int a[3] = {0};
int* p = a;

p[0] = 1; // a[0] = 1
p[1] = 2; // a[1] = 2

a[2] = 3; // p[2] = 3
}

int main()
{
test();

return 0;
}

  • 结论

    • 指针与数组在二进制层面的操作完全不同
    • 指针操作:先寻址,再对地址单元进行操作
    • 数组操作:直接针对地址单元进行操作
  • C/C++编译器的天生缺陷

    • 由4个子部件组成(预处理器,编译器,汇编器,链接器)
    • 每个子部件独立工作,相互之间没有通信
    • 语法规范只在编译阶段有效(如:类型约束,保护成员)
    • 编译器部件对各个源文件进行独立编译(认为源文件相互独立)
  • 问题本质

编程实验:本质分析

  • 解决方案
    • 尽可能不使用跨文件的全局变量(非static全局变量)
    • 当必须使用时,在统一固定的头文件中声明(global.h)
    • 使用跨文件全局变量的源文件直接包含global.h
    • 定义跨文件全局变量的源文件也需要包含global.h
  • 小贴士:声明和定义不同
    • 声明只是告诉编译器,目标存在,可使用
    • 定义实际为目标分配内存(变量)或确定执行流(函数)
    • 理论上,任何目标都需要先声明,再使用
    • C/C++语言允许声明与定义的统一

编程实验:解决方案

小结

  • C/C++语言中的指针与数组在部分情况下可等价
  • 指针与数组在二进制层面的操作完全不同
  • C/C++编译器忽略了源码间的依赖关系
  • 在统一固定的头文件中声明跨文件使用的全局变量(global.h)
  • 使用跨文件全局变量的源文件直接包含global.h